package cn.hinglo.common.util;

import cn.hutool.core.bean.BeanUtil;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.Model;
import com.jfinal.plugin.activerecord.Record;
import com.jfinal.plugin.activerecord.Table;
import com.jfinal.plugin.activerecord.TableMapping;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;


/**
 * @createTime: 2018/11/15 16:23
 * @author: HingLo
 * @description: 系统级别的工具箱
 */
public class SysCommonKit {

    /**
     * 将record集合转为Map集合,用于返回前端数据
     *
     * @param list list
     * @return 返回转为map的集合
     */
    public static List<Map<String, Object>> recordToMapKit(List<Record> list) {
        if (list == null || list.isEmpty()) {
            return null;
        }
        List<Map<String, Object>> newList = new ArrayList<>(list.size());
        list.forEach(x -> {
            toCamelCase(x);
            newList.add(x.getColumns());
        });
        return newList;
    }

    /**
     * 将model集合转为Map集合,用于返回前端数据
     *
     * @param list list
     * @return 返回转为map的集合
     */
    public static List<Map<String, Object>> modelToMapKit(List<? extends Model> list) {
        List<Record> newList = new ArrayList<>(list.size());
        list.forEach(x -> newList.add(x.toRecord()));
        return recordToMapKit(newList);
    }

    /**
     * 将model转为Map在返回,主要目的是移除掉不需返回的参数
     *
     * @param list        model集合
     * @param removeField 需要从Model移除的字段(保证数据安全,减少传递不需要的字段)
     * @return 返回结果
     */
    public static <T extends Model> List<Map<String, Object>> modelToMapKit(List<T> list, String... removeField) {
        if (list.isEmpty()) {
            return null;
        }
        List<Map<String, Object>> newList = new ArrayList<>(list.size());
        // 是否有需要移除的字段
        boolean isRemove = removeField != null && removeField.length > 0;
        list.forEach(x -> {
            Record record = new Record().setColumns(x);
            // 是否移移除字段
            if (isRemove) {
                record.remove(removeField);
            }
            // 将下划线转为驼峰
            toCamelCase(record);
            // 返回结果
            newList.add(record.getColumns());
        });
        return newList;
    }

    /**
     * 将Record对象的key 从下划线命名转为驼峰命名
     *
     * @param record 待转化的Record的对象
     */
    private static void toCamelCase(Record record) {
        String[] columnName = record.getColumnNames();
        for (String s : columnName) {
            String cameName = StrKit.toCamelCase(s);
            // 如果转后的名字不同(名称中有下划线),才进行处理,否则不处理
            if (!s.equals(cameName)) {
                record.set(cameName, record.get(s));
                record.remove(s);
            }
        }
    }

    /**
     * 前端分页返回组装
     *
     * @param total 总条数
     * @param list  数据集合
     * @return 返回结果
     */
    public static Map<String, Object> pageKit(Integer total, List<?> list) {
        Map<String, Object> res = new TreeMap<>();
        res.put("totalSize", total);
        res.put("data", list);
        return res;
    }

    /**
     * 菜单递归遍历
     *
     * @param cowList 原始全部的菜单
     * @return 返回结果
     */
    public static List<Record> createTreeMenuKit(List<Record> cowList) {
        CopyOnWriteArrayList<Record> list = new CopyOnWriteArrayList<>(cowList);
        // 定义父ID的字段名称
        String fid = "fid";
        List<Record> resMenu = new ArrayList<>();
        // 遍历所有的一级菜单
        list.forEach(action -> {
            if (action.getInt(fid) == 0) {
                action.remove(fid);
                resMenu.add(action);
                list.remove(action);
            }
        });
        for (Record menu : resMenu) {
            menu.set("child", getChild(menu.getInt("id"), list, fid));
        }
        return resMenu;
    }

    /**
     * 子菜单递归
     *
     * @param id       id
     * @param rootMenu 菜单目录
     * @return 递归好的菜单
     */
    private static List<Map<String, Object>> getChild(Integer id, CopyOnWriteArrayList<Record> rootMenu, String fid) {
        // 子菜单
        List<Map<String, Object>> childList = new ArrayList<>();
        rootMenu.forEach(menu -> {
            // 遍历所有节点，将父菜单id与传过来的id比较
            if (menu.getInt(fid).equals(id)) {
                menu.remove(fid);
                childList.add(menu.getColumns());
                rootMenu.remove(menu);
            }
        });
        // 把子菜单的子菜单再循环一遍
        childList.forEach(menu -> menu.put("child", getChild((Integer) menu.get(fid), rootMenu, fid)));
        // 递归退出条件
        if (childList.size() == 0) {
            return null;
        }
        return childList;
    }

    /**
     * 移除bean中的指定字段,用于过滤掉不必要字段返回到前端
     *
     * @param list        实体集合
     * @param removeField 移除的字段
     * @param <T>         实体bean
     * @return 返回一个Map 类型的集合
     */
    public <T> List<Map<String, Object>> beanToMapExcludeKit(List<T> list, String... removeField) {
        List<Map<String, Object>> mapList = new ArrayList<>(list.size());
        list.forEach(x -> mapList.add(beanToMapExcludeKit(x, removeField)));
        return mapList;
    }

    /***
     * bean 转为Map
     *
     * @param t 实体bean
     * @param removeField 移除的字段
     * @param <T> bean的类型
     * @return 返回结果
     */
    public <T> Map<String, Object> beanToMapExcludeKit(T t, String... removeField) {
        Map<String, Object> map = BeanUtil.beanToMap(t);
        if (removeField != null && removeField.length > 0) {
            for (String key : removeField) {
                map.remove(key);
            }
        }
        return map;
    }

    /***
     * javabean 过滤 保留制定制定字段
     *
     * @param t 需要过滤的Javabean 对象
     * @param haveField 保留的字段
     * @param <T> Javabean 类型
     * @return 返回结果
     */
    public <T> Map<String, Object> beanToMapHaveKit(T t, String... haveField) {
        Map<String, Object> map = BeanUtil.beanToMap(t);
        Map<String, Object> result = new HashMap<>(map.size());
        if (haveField != null && haveField.length > 0) {
            for (String key : haveField) {
                result.put(key, map.get(key));
            }
        }
        return result;
    }

    /***
     * javabean 过滤 保留制定制定字段
     *
     * @param list T 类型的数据集合
     * @param haveField 保留的字段
     * @param <T> Javabean 类型
     * @return 返回结果
     */
    public <T> List<Map<String, Object>> beanToMapHaveKit(List<T> list, String... haveField) {
        List<Map<String, Object>> mapList = new ArrayList<>(list.size());
        list.forEach(x -> mapList.add(beanToMapHaveKit(x, haveField)));
        return mapList;
    }


    /***
     * 根据Model 来生成默认的SQL,
     * @param model model
     * @param removeField 查询不需要的字段
     * @return 返回一个SQL
     */
    public static String getRemoveFieldSql(Class<? extends Model> model, String... removeField) {
        Table table = TableMapping.me().getTable(model);
        String tableName = table.getName();
        Set<String> column = new HashSet<>(table.getColumnNameSet());
        //移除不需要查询的字段.
        if (removeField != null && removeField.length > 0) {
            for (String field : removeField) {
                column.remove(field);
            }
        }
        // 检测你的Sql是否正确
        if (column.size() < 1) {
            throw new RuntimeException("你的查询SQL不正确,无保留字段");
        }
        // 组装字段
        StringBuilder sqlBuild = new StringBuilder();
        column.forEach(filed -> sqlBuild.append(filed).append(","));
        // 移除最后的一个逗号
        String substring = sqlBuild.substring(0, sqlBuild.length() - 1);
        return "select " + substring + " from " + tableName;
    }

    /***
     * 根据SQL与Record来组装条件
     * @param sql 表名前部分
     * @param record 条件部分(key-value 形式,key 表示字段名称,value 表示类型(=,> < ))
     * @return 返回带有条件的查询SQL,
     */
    public static String getWhereSql(String sql, Record record) {
        Map<String, Object> columns = record.getColumns();
        StringBuilder sqlBuild = new StringBuilder(sql).append(" where ");
        columns.forEach((key, value) -> sqlBuild.append(key).append(" ").append(value).append(" ? and "));
        return sqlBuild.substring(0, sqlBuild.length() - 4);
    }

    /***
     * 保留指定的字段查询的SQL,如:"cron,group_name,name,id,clazz,status"
     * @param model model
     * @param field 字段
     * @return 返回结果
     */
    public static String getHavingFieldSql(Class<? extends Model> model, String field) {
        Table table = TableMapping.me().getTable(model);
        String tableName = table.getName();
        return "select " + field + " from " + tableName;
    }
}
